package UMC.Data.Sql;


import UMC.Data.Database;
import UMC.Data.Utility;

import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.*;


public abstract class Initializer {

    static HashSet<Initializer> _Initializers = new HashSet<>();

    public static Initializer[] initializers() {
        Initializer[] Initializers = new Initializer[_Initializers.size()];
        _Initializers.toArray(Initializers);
        return Initializers;
    }

    public static void put(Initializer initializer) {
        if (_Initializers.stream().filter(r -> r.name().equals(initializer.name())).count() == 0) {
            _Initializers.add(initializer);
        }
    }

    @Override
    public int hashCode() {
        String n = this.name();
        if (Utility.isEmpty(n)) {
            return 0;
        }
        return n.hashCode();
    }

    public abstract String name();

    public abstract String caption();


    public String providerName() {
        return null;
    }


    public void setup() {

    }


    public void upgrade() {

    }


    private final Map<Object, String[]> dictionary = new LinkedHashMap<>();

    protected void setup(Object key) //where T : Record
    {
        dictionary.put(key, new String[0]);
    }

    protected void setup(Object key, Object text)//where T : Record
    {
        dictionary.put(key, GetValues(text).keySet().toArray(new String[0]));

    }

    protected void setup(Object key, String... fields)//where T : Record
    {
        dictionary.put(key, fields);

    }


    public void drop(DbProvider provider) {

        Database factory = Database.instance(provider);
        ISqler sqler = factory.sqler();

        for (Object o : dictionary.keySet()) {
            String tabName = o.getClass().getSimpleName();
            info("删除表", tabName);
            tabName = GetName(provider, tabName);
            StringBuilder sb = new StringBuilder();
            sb.append("DROP TABLE ");
            sb.append(tabName);
            try {
                sqler.executeNonQuery(sb.toString());
            } catch (Exception ex) {
                info(ex.getMessage());
            }
        }
    }

    String GetName(DbProvider provider, String tabName) {
        String Delimiter = provider.delimiter();

        String QuotePrefix = provider.quotePrefix();
        String QuoteSuffix = provider.quoteSuffix();
        String Prefixion = provider.prefixion();

        if (Utility.isEmpty(Delimiter)) {
            tabName = String.format("%s%s%s", QuotePrefix, provider.builder().Column(tabName), QuoteSuffix);
        } else {
            if (Utility.isEmpty(Prefixion)) {
                tabName = String.format("%s%s%s", QuotePrefix, provider.builder().Column(tabName), QuoteSuffix);
            } else {
                if (".".equals(Delimiter)) {
                    tabName = String.format("%s%s%s.%s%s%s", QuotePrefix, Prefixion, QuoteSuffix, QuotePrefix, provider.builder().Column(tabName), QuoteSuffix);
                } else {
                    tabName = String.format("%s%s%s%s%s", QuotePrefix, Prefixion, Delimiter, provider.builder().Column(tabName), QuoteSuffix);
                }
            }

        }
        return tabName;
    }

    public void upgrade(DbProvider provider) {

        DbBuilder builder = provider.builder();
        if (builder == null) {
            info("此数据库节点配置器未有管理器");
            return;
        }
        Database factory = Database.instance(provider);
        ISqler sqler = factory.sqler(false);

        for (Map.Entry<Object, String[]> entry : dictionary.entrySet()) {
            CheckTable(sqler, provider, entry.getKey(), entry.getValue());
        }

    }

    void info(String... strings) {
        for (String string : strings) {
            System.out.printf(string);
            System.out.printf("  ");
        }
        System.out.printf("\r\n");

    }


    void CheckTable(ISqler sqler, DbProvider provider, Object key, String[] textFields) {


        String tabName = key.getClass().getSimpleName();
        info("检测表", tabName);

        DbBuilder builder = provider.builder();


        tabName = GetName(provider, tabName);
        String QuotePrefix = provider.quotePrefix();
        String QuoteSuffix = provider.quoteSuffix();

        String checkName = tabName;
        if (!Utility.isEmpty(QuotePrefix)) {
            checkName = checkName.replace(QuotePrefix, "");
        }
        if (!Utility.isEmpty(QuoteSuffix)) {
            checkName = checkName.replace(QuoteSuffix, "");
        }

        if (!builder.Check(checkName, sqler)) {
            CreateTable(sqler, provider, key, textFields);
        } else {
            Field[] ps = key.getClass().getFields();
            for (Field property : ps) {

                String filed = String.format("%s%s%s ", QuotePrefix, builder.Column(property.getName()), QuoteSuffix);
                String cfiled = builder.Column(filed);

                String checkField = cfiled;

                if (!Utility.isEmpty(QuotePrefix)) {
                    checkField = checkField.replace(QuotePrefix, "");
                }
                if (!Utility.isEmpty(QuoteSuffix)) {
                    checkField = checkField.replace(QuoteSuffix, "");
                }
                if (!builder.Check(checkName.trim(), checkField.trim(), sqler)) {
                    StringBuilder sb = new StringBuilder();
                    Class type = property.getType();

                    switch (type.getName()) {
                        case "java.lang.Integer":
                        case "java.lang.Short":
                        case "java.lang.Byte":
                            sb.append(builder.AddColumn(tabName, cfiled, builder.Integer()));
                            break;
                        case "java.lang.Float":
                        case "java.lang.Double":
                            sb.append(builder.AddColumn(tabName, cfiled, builder.Float()));
                            break;
                        case "java.lang.Long":
                            sb.append(builder.AddColumn(tabName, cfiled, builder.Number()));
                            break;
                        case "java.lang.Boolean":
                            sb.append(builder.AddColumn(tabName, cfiled, builder.Boolean()));
                            break;
                        case "java.util.Date":
                            sb.append(builder.AddColumn(tabName, cfiled, builder.Date()));
                            break;
                        case "java.util.UUID":
                            sb.append(builder.AddColumn(tabName, cfiled, builder.Guid()));
                            break;
                        default:
                            if (type.isEnum()) {
                                sb.append(builder.AddColumn(tabName, cfiled, builder.Integer()));
                            } else if (type == byte[].class) {
                                // class.
                                sb.append(builder.AddColumn(tabName, cfiled, builder.Binary()));
                            } else if (Utility.exists(textFields, r -> r.equals(property.getName()))) {

                                sb.append(builder.AddColumn(tabName, cfiled, builder.Text()));
                            } else {
                                sb.append(builder.AddColumn(tabName, cfiled, builder.String()));
                            }
                            break;
                    }
                    if (sb.length() > 0) {
                        info("追加表字段", tabName + '.' + cfiled);
                        ExecuteNonQuery(sqler, sb.toString());
                    }
                }
            }
        }

    }

    void ExecuteNonQuery(ISqler sqler, String sb) {
        try {
            sqler.executeNonQuery(sb);
        } catch (Exception ex) {
            info(sb);
            info(ex.getMessage());
        }


    }

    static Map<String, Object> GetValues(Object record) {
        Map<String, Object> vs = new LinkedHashMap<>();
        Field[] fields = record.getClass().getFields();

        for (Field field : fields) {

            field.setAccessible(true);
            Object v = null;
            try {
                v = field.get(record);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            if (v == null) {
                continue;
            }
            vs.put(field.getName(), v);
        }
        return vs;
    }

    void CreateTable(ISqler sqler, DbProvider provider, Object key, String[] textFeilds) {


        String tabName = key.getClass().getSimpleName();


        Map<String, Object> keys = GetValues(key);

        String Delimiter = provider.delimiter();
        String Prefixion = provider.prefixion();
        String QuotePrefix = provider.quotePrefix();
        String QuoteSuffix = provider.quoteSuffix();
        DbBuilder builder = provider.builder();

        StringBuilder sb = new StringBuilder();
        sb.append("CREATE TABLE ");
        sb.append(tabName);
        sb.append("(");
        Field[] ps = key.getClass().getFields();
        for (Field property : ps) {
            String filed = String.format("%s%s%s ", QuotePrefix, builder.Column(property.getName()), QuoteSuffix);

            sb.append(builder.Column(filed));
            Class type = property.getType();


            switch (type.getName()) {
                case "java.lang.Integer":
                case "java.lang.Short":
                case "java.lang.Byte":
                    sb.append(builder.Integer());
                    break;
                case "java.lang.Float":
                case "java.lang.Double":
                    sb.append(builder.Float());
                    break;
                case "java.lang.Long":
                    sb.append(builder.Number());
                    break;
                case "java.lang.Boolean":
                    sb.append(builder.Boolean());
                    break;
                case "java.util.Date":
                    sb.append(builder.Date());
                    break;
                case "java.util.UUID":
                    sb.append(builder.Guid());
                    break;
                default:
                    if (type.isEnum()) {
                        sb.append(builder.Integer());
                    } else if (type == byte[].class) {
                        sb.append(builder.Binary());
                    } else if (Utility.exists(textFeilds, r -> r.equals(property.getName()))) {

                        sb.append(builder.Text());
                    } else {
                        sb.append(builder.String());
                    }
                    break;
            }
            if (keys.containsKey(property.getName())) {
                sb.append(" NOT NULL");
            }
            sb.append(",");
        }
        sb.deleteCharAt(sb.length() - 1);

        sb.append(")");
        try {
            sqler.executeNonQuery(sb.toString());
        } catch (Exception ex) {
            info("Setup", sb.toString());
            info(ex.getMessage());
        }
        if (keys.size() > 0) {
            List<String> ids = new LinkedList<>();
            for (String s : keys.keySet()) {
                String filed = String.format("%s%s%s", QuotePrefix, s, QuoteSuffix);
                ids.add(filed);
            }
            String sql = builder.PrimaryKey(tabName, ids.toArray(new String[0]));
            if (!Utility.isEmpty(sql)) {
                try {
                    sqler.executeNonQuery(sql);
                } catch (Exception ex) {
                    info("创建主键", String.join(",", ids.toArray(new String[0])), ex.getMessage());
                }
            }
        }


    }

    public void setup(DbProvider provider) {
        DbBuilder builder = provider.builder();
        if (builder == null) {
            info("此数据库节点配置器未有管理器");
            info("操作结束");
            return;
        }
        Database factory = Database.instance(provider);
        ISqler sqler = factory.sqler(false);

        String Delimiter = provider.delimiter();
        String Prefixion = provider.prefixion();
        info("数据前缀", Prefixion);
        if (".".equals(Delimiter) && !Utility.isEmpty(Prefixion)) {
            String prefix = String.format("%s%s%s", provider.quotePrefix(), Prefixion, provider.quoteSuffix());
            String schemaSQL = builder.Schema(prefix);
            if (!Utility.isEmpty(schemaSQL)) {
                try {
                    sqler.executeNonQuery(schemaSQL);
                } catch (Exception ex) {
                    info(ex.getMessage());
                }
            }
        }

        for (Map.Entry<Object, String[]> entry : dictionary.entrySet()) {
            CheckTable(sqler, provider, entry.getKey(), entry.getValue());
        }

    }
}

